import os
import pty
import shutil
from subprocess import Popen

from tinyops.library.helper import detect_shell, process_output_stream
from tinyops.library.task import TaskLogger, TaskEnv

from .helper import ExportHelper


class LocalExecutor:

    def __init__(self, env: TaskEnv, timeout: int, logger: TaskLogger):
        self.name = 'local'
        self._log = logger.target(self.name).log
        self.env = env if env is not None else TaskEnv()
        self.timeout = timeout
        self.logger = logger
        self.shell = None
        self._export_helper = ExportHelper(
            env=self.env,
            logger=logger.target(self.name),
            handler=self._export_handler,
        )
        self._inited = False

    def _auto_detect_shell(self):
        try:
            with open('/etc/shells') as f:
                content = f.read()
        except (FileNotFoundError, IOError, OSError):
            content = None
        return detect_shell(content)

    def init(self):
        if self._inited:
            return
        self._inited = True
        self.shell = self._auto_detect_shell()

    def close(self):
        pass

    def _export_handler(self, export_file):
        content = None
        try:
            with open(export_file) as f:
                content = f.read()
        except FileNotFoundError:
            self._log(f'EXPORT failed, export file not exists: {export_file}')
        except IOError as ex:
            self._log(f'EXPORT failed, can not read export file: {ex}')
        shutil.rmtree(export_file, ignore_errors=True)
        return content

    def do_run(self, script: str, exports: list = None):
        self._log(script, sep='>')
        script = 'set -e;\n' + script
        script = self._export_helper.before_run(script, exports)
        cmd = [self.shell, '-c', script]
        master, slave = pty.openpty()
        p = Popen(cmd, stdout=slave, stderr=slave, env=self.env.value())
        os.close(slave)
        outputs = []
        try:
            for msg in process_output_stream(master, timeout=self.timeout):
                outputs.append(msg)
                self._log(msg)
        finally:
            os.close(master)
            if p.poll() is None:
                p.kill()
            p.wait()
        self._log(exit_code=p.returncode)
        if not self._export_helper.after_run(outputs):
            return False
        return p.returncode == 0
